<?php

namespace aliyun;

use OSS\OssClient;
use OSS\Core\OssException;
use think\File;
use think\file\UploadedFile;
use think\exception\FileException;

use AlibabaCloud\Client\AlibabaCloud;
use Exception;

/**
 * 阿里云OSS操作类
 * 如需使用请 composer require aliyuncs/oss-sdk-php
 */
class AliyunOss
{
	/**
	 * 上传一个本地文件到OSS
	 * @param string|File|UploadedFile $file 文件绝对路径、File实例，或UploadedFile实例
	 * @param string $directory OSS目录，例如 'image/'，注意还会读取config中的aliyun.oss.rootpath作为根路径
	 * @param boolean $unlink 是否上传后删除
	 * @param boolean $forceReplace 是否强制上传（默认false，会自动检查文件是否存在，若存在oss则不上传）
	 * @param boolean $useSSL 是否以https方式调用（返回的url会有区别）
	 * @return array [url, hash]
	 * @throws OssException|FileException|Exception
	 */
	public static function sendFile($file, string $directory, bool $unlink = false, $forceReplace = false, $useSSL = true)
	{
		$file = static::decodeFile($file);

		$bucket = config('aliyun.oss.bucket');
		$endpoint = config('aliyun.oss.endpoint');

		$uploadPath = config('aliyun.oss.rootpath') . $directory;

		// 如果绑定了cdn域名
		$cdnDomain = config('aliyun.oss.cdn_domain');

		// hashName首次调用必须指定一种hash算法，否则默认会以时间戳生成，跟文件内容无关
		// hashName方法会在前两位后加一个斜杠，删除这个
		$fileSaveName = str_replace(DIRECTORY_SEPARATOR, '', $file->hashName('sha256'));

		// oss客户端
		$ossClient = new OssClient(config('aliyun.accessKeyId'), config('aliyun.accessKeySecret'), $endpoint);

		// https
		$ossClient->setUseSSL($useSSL);

		$result = null;

		// 检查是否存在
		if (!$forceReplace) {
			try {
				$checkInfo = $ossClient->getObjectMeta($bucket, $uploadPath . $fileSaveName);
			} catch (\Exception $e) {
				$checkInfo = null;
			}
			if ($checkInfo) {
				$result = ['url' => $checkInfo['oss-request-url'], 'hash' => $fileSaveName];
			}
		}

		if (!$result) {
			$saveInfo = $ossClient->uploadFile($bucket, $uploadPath . $fileSaveName, $file->getPathname());
			$result = ['url' => $saveInfo['oss-request-url'], 'hash' => $fileSaveName];
		}

		if ($unlink) {
			try {
				unlink($file->getPathname());
			} catch (Exception $e) {
			}
		}

		// 如果绑定了cdn域名，则从$result['url']中替换掉oss的域名
		if ($cdnDomain) {
			// 处理cdnDomain结尾的/
			$cdnDomain = rtrim($cdnDomain, '/');
			$ossUrl = $result['url'];
			$ossHost = parse_url($ossUrl, PHP_URL_HOST);
			$ossScheme = parse_url($ossUrl, PHP_URL_SCHEME);
			$ossDomain = $ossScheme . '://' . $ossHost;
			// 替换掉 oss 域名部分
			$result['url'] = preg_replace('#^' . preg_quote($ossDomain, '#') . '#', $cdnDomain, $ossUrl);
		}

		unset($file);

		return $result;
	}

	/**
	 * 获取文件
	 * @param [type] $file
	 * @return File
	 * @throws Exception
	 */
	protected static function decodeFile($file)
	{
		if ($file instanceof UploadedFile || $file instanceof File) {
			return $file;
		} else if (is_string($file)) {
			return new File($file);
		} else {
			throw new Exception('AliyunOss 要上传的文件不合法');
		}
	}

	/**
	 * 获取Sts临时访问凭证
	 * 本角色仅限 Oss完全权限
	 * @param $expireTime 过期时间（秒），最小15分钟，最大1小时
	 */
	public static function getOssSts($expireTime = 3600)
	{
		$roleArn = 'acs:ram::1245343262379076:role/oss-uploader';
		$roleSessionName = 'upload-video-to-oss'; // 自定义的会话ID

		AlibabaCloud::accessKeyClient(config('aliyun.accessKeyId'), config('aliyun.accessKeySecret'))
			->regionId(config('aliyun.sts.regionId'))
			->asDefaultClient();
		$result = AlibabaCloud::rpc()
			->product('Sts')
			->scheme('https') // https | http
			->version('2015-04-01')
			->action('AssumeRole')
			->method('POST')
			->host('sts.aliyuncs.com')
			->options([
				'query' => [
					'RegionId' => config('aliyun.sts.regionId'),
					'RoleArn' => $roleArn,
					'RoleSessionName' => $roleSessionName,
					'DurationSeconds' => $expireTime
				],
			])
			->request();

		// ['SecurityToken', 'AccessKeyId', 'AccessKeySecret', 'Expiration'=>'2022-02-27T06:29:26Z']
		return array_merge($result->toArray()['Credentials'], [
			'Bucket' => config('aliyun.oss.bucket'),
			'Region' => config('aliyun.sts.regionId'),
			'Endpoint' => config('aliyun.oss.endpoint'),
		]);
	}

	/**
	 * 获取Oss临时访问的查询query
	 * @param string $resourceUrl 资源路径(https://gwxt-oss.oss-cn-shanghai.aliyuncs.com/course/contract/aa.png)
	 * @param integer $expireTime 过期时长（秒）
	 * @return string
	 */
	public static function getOssAccessQuery($resourceUrl, $expireTime = 3600)
	{
		$sts = static::getOssSts();
		$ossClient = new OssClient($sts['AccessKeyId'], $sts['AccessKeySecret'], $sts['Endpoint'], false, $sts['SecurityToken']);
		$objectUrl = implode('/', array_slice(explode('/', $resourceUrl), 3));
		return $ossClient->signUrl(config('aliyun.oss.bucket'), $objectUrl, $expireTime);
	}
}
